﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using LLoger;
using OpenPop.Mime;
using OpenPop.Mime.Header;
using OpenPop.Pop3;


namespace TSRepLoader.Pop
{
    class PopWrapper:IDisposable
    {
        private struct IndexedMessageHeader
        {
            public int Index;
            public MessageHeader MessageHeader;

            public IndexedMessageHeader(int index, MessageHeader hdr)
            {
                Index = index;
                MessageHeader = hdr;
            }

            public static explicit operator MessageHeader(IndexedMessageHeader iMsg)
            {
                return iMsg.MessageHeader;
            }
        }

        private readonly string _host;
        private readonly int _port;
        private readonly bool _ssl;
        private readonly string _user;
        private readonly string _pass;
        private readonly ILogWriter _logger;
        private int _loadMsgCnt;
        private int _currentPos = 0;
        private Pop3Client _client = null;
        private Dictionary<IndexedMessageHeader, Message> _messageCache = new Dictionary<IndexedMessageHeader, Message>();
        private bool _headersWasLoaded = false;

        private object _synh = new object();
        private int _msgCnt;

        private Pop3Client Client
        {
            get
            {
                LazyInit();
                

                return _client;
            }
        }

        private void LazyInit()
        {

            if (_client == null)
            {
                _client = new Pop3Client();
            }

            if (!_client.Connected)
                try
                {
                    _client.Connect(_host, _port, _ssl);
                    _client.Authenticate(_user, _pass);

                }
                catch (Exception ex)
                {
                    _logger.WriteLine(ex, "Не удалось установить связь с почтовым сервером: " + _host);
                    throw;
                }
        }

        /// <summary>
        /// Сброс в начальное состояние для новой загрузки
        /// </summary>
        public void Reset()
        {
            _messageCache.Clear();
            _headersWasLoaded = false;
        }

        private void LoadHeaders(int count = 300)
        {
            if (_headersWasLoaded)
                return;

            _msgCnt = Client.GetMessageCount();
            _loadMsgCnt = count;
            _loadMsgCnt = _loadMsgCnt > _msgCnt ? _msgCnt : _loadMsgCnt;

            int loadTo = _msgCnt - _loadMsgCnt;

            for (int i = _msgCnt; i > loadTo; i--)
                try
                {
                    _messageCache.Add(new IndexedMessageHeader(i, Client.GetMessageHeaders(i)), null);
                }
                catch (Exception ex)
                {
                    _logger.WriteLine(ex, "Загрузка заголовков");
                }

            _headersWasLoaded = true;
        }

        public PopWrapper(string host, int port, bool ssl, string user, string pass, ILogWriter logger = null)
        {
            _host = host;
            _port = port;
            _ssl = ssl;
            _user = user;
            _pass = pass;
            _logger = logger ?? new DummyLog();

        }

        public IEnumerable<Message> FindMsg(DateTime date, string fromPart=null, string subj=null,string msgId=null, int loadDepth = 300)
        {
            LoadHeaders(loadDepth);

            var candidates = from hdr in _messageCache.Keys
                             let mh = hdr.MessageHeader
                             where mh.DateSent.Date == date
                               && (
                                    (string.IsNullOrWhiteSpace(fromPart) || mh.From.Address.ToLower().Contains(fromPart.ToLower()))
                                    &&
                                    (string.IsNullOrWhiteSpace(subj) || mh.Subject.ToLower().Contains(subj.ToLower()))
                                    &&
                                    (string.IsNullOrWhiteSpace(msgId) || mh.MessageId.ToLower().Contains(msgId.ToLower()))
                               )
                             select hdr;

            if (!candidates.Any())
                return null;

            candidates = candidates.ToList();

            List<Message> _foundMsg = new List<Message>();
            foreach (var imsg in candidates)
            {
                Message msg = null;
                try
                {
                    if (!_messageCache.ContainsKey(imsg) || _messageCache[imsg] == null)
                        msg = _messageCache[imsg] = Client.GetMessage(imsg.Index);
                }
                catch (Exception ex)
                {
                    _logger.WriteLine(ex, "Загрузка сообщения");
                }

                _foundMsg.Add(msg);
            }

            return _foundMsg;

        }

        public IEnumerable<MessageHeader> GetHdrs()
        {
            LoadHeaders();
            if (_messageCache != null && _messageCache.Count > 0)
                return _messageCache.Select(m => m.Key.MessageHeader);

            return null;
        }

        public void Dispose()
        {
            if (_client != null && _client.Connected)
            {
                _client.Disconnect();
                _client.Dispose();
            }
        }
    }
}
